package com.pan.kang.security;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.pan.kang.common.Response;
import com.pan.kang.common.jwt.JwtConstants;
import com.pan.kang.common.jwt.JwtUser;
import com.pan.kang.common.jwt.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import static com.pan.kang.security.PermissionMatcher.checkPermission;

@Component
@Order(0)
@WebFilter(urlPatterns = {"/**"})
@Slf4j
public class TokenAuthorFilter implements Filter {

    public static List<String> ignoreUrl = new ArrayList();


    @Value("${spring.security.ignores:}")
    private String ignoreUrls;

    @Value("${spring.security.login:false}")
    private Boolean loginCheck;

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        ignoreUrl.addAll(StrUtil.split(this.ignoreUrls, ','));
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        Response response = null;
        // 是否需要登录检查
        if (this.loginCheck) {
            HttpServletRequest req = (HttpServletRequest) servletRequest;
            HttpServletResponse rep = (HttpServletResponse) servletResponse;
            rep.setHeader("Access-Control-Allow-Origin", "*");
            rep.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH");
            rep.setHeader("Access-Control-Max-Age", "3600");
            rep.setHeader("Access-Control-Allow-Headers", "token,Origin, X-Requested-With, Content-Type, Accept");
            servletResponse.setCharacterEncoding("UTF-8");
            servletResponse.setContentType("application/json; charset=utf-8");
            String path = req.getServletPath();
            if (!checkPermission(ignoreUrl, path)) {
                // 判断请求头是否传 token
                String token = req.getHeader("Authorization");
                if (token == null || token.isEmpty()) {
                    response = new Response("用户未登录！！！", 500);
                } else {
                    // 验证 token 是否有效
                    JwtUser jwtUser = JwtUtil.getJwtUser(token);
                    if (jwtUser == null) {
                        response = new Response("用户未登录！！！", 500);
                    } else {
                        // 判断 token 是否过期
                        String redis_token = stringRedisTemplate.opsForValue()
                                .get(JwtConstants.JWT_TOKEN_KEY.concat(jwtUser.getUserId()));
                        if (redis_token == null || redis_token.isEmpty()) {
                            response = new Response("token 过期！！！", 550);
                        }
                    }
                }
            }
        }
        if (response == null) {
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            // 这里抛异常，全局异常捕获不到，直接通过流的形式响应
            PrintWriter writer = null;
            OutputStreamWriter osw = null;
            try {
                String result = JSONUtil.toJsonStr(response);
                HttpServletResponse rep = (HttpServletResponse) servletResponse;
                osw = new OutputStreamWriter(rep.getOutputStream(), "UTF-8");
                writer = new PrintWriter(osw, true);
                writer.write(result);
                writer.flush();
                writer.close();
                osw.close();
            } catch (Exception e) {
                log.error("过滤器返回信息失败:" + e.getMessage(), e);
            } finally {
                if (null != writer) {
                    writer.close();
                }
                if (null != osw) {
                    osw.close();
                }
            }
        }
    }

    @Override
    public void destroy() {
    }


}
